<?php

    if (!defined('__IS_LEAF__')) exit('Access Denied!');
    /**
     * 应用处理类
     * 检测应用（或分组）配置，生成应用（或分组），设置运行模式（app、group），根据不同运行模式调用路由器
     *    此类中，setAppInfo设置app相关的常量等内容，同理setGroupInfo方法
     */
    class App {

        static private $appInfo=array();
        static private $runMode;

        /**
         * 应用初始化，主要工作：
         * 1、检查app.ini或group是否正确
         * 2、将约定app或group配置和用户自定义app配置合并（自定义优先）根据配置生成app.ini或group.ini的所有应用
         * 3、将用合并后的app或group配置和系统配置再次合并然后放到全局变量的app下，此处如果是app.ini，则结果是当前app信息（并非app.ini中所有的）
         * 4、运行应用或分组，调用路由器
         * @access public
         */
        static public function init(){
            self::checkAppIni();            //检测应用配置，此次获取的是app.ini所有配置或默认约定值
            self::buildApp();                //根据自身类的$appInfo的值，生成项目
            self::runInit();                //先调用合并应用或分组配置的操作，然后运行app或分组
        }

        /**
         * 检测应用配置是否正确，如正确，将解析后的结果放到$appInfo中
         * @access private
         */
        static private function checkAppIni(){
            $appConfigFile = __APPROOT__.__SP__.'config.php';
            //解析应用配置文件，不存在，则获取默认的约定配置
            file_exists_case($appConfigFile)? $appConfig=require $appConfigFile : error(L('appini_not_exists'));
            //全局加载计量器+1
            addLoadedFile($appConfigFile);
            //判断App配置是否正确
            if(is_array($appConfig)){
                $appInfo = count($appConfig)>0 ? $appConfig : self::getDefaultAppIni();
                //处理app.ini中定义的app的path信息，判断是否为绝对路径，绝对路径不做转化，只做realpath处理，非绝对路径转化为绝对路径
                foreach ($appInfo as $appKey => &$appValue) {
                    //如果没有在app.ini中定义应用的入口，则抛出错误
                    empty($appValue['boot']) && error(L('boot_not_defined'));
                    if(file_exists_case($appValue['path'])){
                        //将app.ini中定义的路径，转化为真实的路径，如果是决定路径，则
                        $appValue['path'] = realpath($appValue['path']).__SP__.$appKey;
                    }else{
                        $appValue['path'] = __APPROOT__.__SP__.$appKey;
                    }
                    $appValue['name'] = $appKey;
                }
                //解析后的结果放到$appInfo中，如果定义了多个应用，则此时包含的是多个应用的信息
                self::$appInfo = $appInfo;
            }
        }

        /**
         * 当没有任何应用配置的时候，获取默认的配置，默认配置的应用为home
         * @access private
         * @return array $appConfig 返回数组类型的默认APP配置
         */
        static private function getDefaultAppIni(){
            $appConfig = array(
                'home'=>array(
                    'boot'=>'index.php',
                    'path'=>'./',
                    'tpl_suffix'=>'html',
                    'leaf_sys_cache'=>'1',
                    'leaf_app_cache'=>'1',
                    'cache_type'=>'file',
                    'error_record'=>'0',
                )
            );
            return $appConfig;
        }

        /**
         * 根据配置，生成应用，此次是一次生成配置app.ini中的所有应用。遵循约定大于配置原则
         * @access private
         */
        static private function buildApp(){
            //待首次被创建的应用的目录和文件结构
            $buildArr = array(
                'function'=>'function.php',
                'libs/controller/'=>'indexController.php',
                'libs/service'=>'',
                'tpl/index/'=>'index.'.C('tpl_suffix'),
                'lang'=>'lang.Cn.ini',
                '_runtime'=>'',
                'addons'=>'',
            );
            //创建目录和文件
            foreach (self::$appInfo as $appKey => $appValue) {
                //如果应用目录存在，则略过，不再创建，以免由于覆盖造成整个项目丢失
                if(!file_exists_case($appValue['path'])){
                    foreach ($buildArr as $buildKey => $buildValue) {
                        //目录
                        $dir = $appValue['path'].__SP__.$buildKey;
                        //创建文件，如果失败，抛出错误
                        !mkdir($dir, 0755, true) && error(L('build_dir_failure').'：'.$dir);
                        //如果值$buildValue不为空，则创建文件
                        if(!empty($buildValue)){
                            $file = $dir.__SP__.$buildValue;
                            !touch($file) && error(L('build_file_failure').'：'.$file);
                            if($buildValue == 'indexController.php'){
                                $controllerTpl = __LEAF_COMMON__.__SP__.'tpl'.__SP__.'controller.tpl';
                                file_put_contents($file, file_get_contents($controllerTpl));
                            }
                        }
                    }
                }
            }
        }

        /**
         * 合并应用配置和系统配置，此次合并的结果是全局变量app的值将单一，根据入口文件设置对应的应用配置到全局变量中
         */
        static private function mergeAppConfig(){
            //通过路由器，获取正确的入口
            $boot = Router::getBoot();
            //循环设置全局信息
            foreach (self::$appInfo as $appKey =>&$appValue) {
                //获取用户配置
                $userAppConfig = self::$appInfo[$appKey];
                //获取默认配置
                $defaultAppConfig = self::getDefaultAppIni();
                //只取用默认配置中，除path的部分配置
                unset($defaultAppConfig['home']['path']);
            }
            //合并配置
            $appValue = array_merge($defaultAppConfig['home'], $userAppConfig);
            //将app配置信息合并到全局变量的app中
            if($appValue['boot']==$boot){
                C('app', $appValue, true);
                global $leaf;
                $leaf['config'] = array_merge($leaf['config'], $appValue);
                self::$appInfo = $appValue;
            }
        }

        /**
         * 判断有没有分组信息，运行应用或分组的初始化操作
         * @access private
         */
        static public function runInit(){
            self::checkRunMode();
            $run = self::$runMode.'Run';
            self::$run();
        }

        /**
         * 检测运行模式是应用还是分组
         * @access private
         */
        static private function checkRunMode(){
            //判断是不是配置了分组模式，也就是判断全局数组有没有group信息
            //如果没有分组信息，按应用模式进行
            self::$runMode = (is_array($group=GL('group')) && count($group)>0) ? 'group' : 'app';
        }

        /**
         * 设置app相关信息，并运行app，路由器也是由此调用的
         * @access private
         */
        static private function appRun(){
            //合并配置操作，将替换原来全局变量中的app的值为实际应用配置，而非所有应用配置
            self::mergeAppConfig();
            $appRunInfo = Router::routeInit('app');
            //设置
            self::setAppInfo($appRunInfo);
            $action = $appRunInfo['controller'];
            $method = $appRunInfo['action'];
            //实例化并运行app中的模块
            $model = N($action);
            $model->$method();
        }

        /**
         * 运行group分组
         * @access private
         */
        static private function groupRun(){
            Router::routeInit('groupRun');
        }

        /**
         *    appRun()调用的方法
         * 设置应用信息：常量、配置，合并app全局信息和路由返回的调用信息（主要是controlller名和action）等，此处可后期优化设置缓存
         * @access private
         */
        static private function setAppInfo($appRunInfo){
            self::$appInfo = array_merge(self::$appInfo, $appRunInfo);
            C('app', self::$appInfo, true);
            define('__APP__', self::$appInfo['name']);
            define('__CONTROLLER__', self::$appInfo['controller']);
            define('__ACTION__', self::$appInfo['action']);
            define('__APP_PATH__', self::$appInfo['path']);
            define('__TPL_PATH__', __APP_PATH__.__SP__.'tpl');
            define('__CONTROLLER_PATH__', __APP_PATH__.__SP__.'libs'.__SP__.'controller');
            define('__SERVICE_PATH__', __APP_PATH__.__SP__.'libs'.__SP__.'service');
            define('__LANG_PATH__', __APP_PATH__.__SP__.'lang');
            define('__FUNCTION_PATH__', __APP_PATH__.__SP__.'function');
        }

    }